﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PacketDotNet;
using System.Windows.Forms;
using SharpPcap;
using System.Net;

namespace TinyIM2013
{
    public class TinyPacket
    {
        public bool IsRedirectServerPacket = false;
        public static string[] UDPServerIpList;
        public static string[] TCPServerIpList;
        public static ushort[] TCPServerPortList = { 443 };
        public static ushort[] UDPServerPortList = { 8000 };
        public static string[] UDPServerDomainList = { "183.60.48.174", "sz2.tencent.com", "sz3.tencent.com", "sz4.tencent.com", "sz5.tencent.com", "sz6.tencent.com", "sz7.tencent.com" };
        public static string[] TCPServerDomainList = { "183.60.48.171", "tcpconn2.tencent.com", "tcpconn3.tencent.com", "tcpconn4.tencent.com" };
        private static bool IsListViewInitiated = false;
        private static log4net.ILog AppLog = log4net.LogManager.GetLogger("TinyIM.Application");
        private static log4net.ILog NetworkLog = log4net.LogManager.GetLogger("TinyIM.Network");
        public TinyPacket Self { get; set; }
        public RawCapture RawCapture { get; set; }
        public IPProtocolType ProtocolType { get; set; }
        public DateTime Time { get; set; }
        public byte[] PayloadData { get; set; }
        public string DestinationIp { get; set; }
        public string SourceIp { get; set; }

        public string DestinationAddress { get; set; }
        public string SourceAddress { get; set; }

        public uint DestinationPort { get; set; }
        public uint SourcePort { get; set; }

        public static string[] GetIpByDomain(string[] serverList)
        {
            int count = serverList.Count();
            string[] serverIpList = new string[count];
            for (int i = 0; i < count; i++)
            {
                try
                {
                    IPHostEntry hostEntry = Dns.GetHostEntry(serverList[i]);
                    IPEndPoint ipEndPoint = new IPEndPoint(hostEntry.AddressList[0], 0);
                    serverIpList[i] = ipEndPoint.Address.ToString();
                }
                catch
                {
                    serverIpList[i] = "0.0.0.0";
                }

            }
            return serverIpList;
        }
        public bool IsTinyIMPacket
        {
            get
            {
                if (ProtocolType == IPProtocolType.UDP)
                {
                    if (UDPServerIpList == null)
                    {
                        UDPServerIpList = GetIpByDomain(UDPServerDomainList);
                    }
                    foreach (string udpServerIp in UDPServerIpList)
                    {
                        if (Self.SourceIp == udpServerIp || Self.DestinationIp == udpServerIp)
                        {
                            foreach (ushort udpServerPort in UDPServerPortList)
                            {
                                if (Self.SourcePort == udpServerPort || Self.DestinationPort == udpServerPort)
                                {
                                    if ((Self.SourceIp == udpServerIp && Self.SourcePort == udpServerPort) || (Self.DestinationIp == udpServerIp && Self.DestinationPort == udpServerPort))
                                    {
                                        return true;
                                    }
                                }
                            }
                        }
                    }
                }
                else if (ProtocolType == IPProtocolType.TCP)
                {
                    if (TCPServerIpList == null)
                    {
                        TCPServerIpList = GetIpByDomain(TCPServerDomainList);
                    }
                    foreach (string tcpServerIp in TCPServerIpList)
                    {
                        if (Self.SourceIp == tcpServerIp || Self.DestinationIp == tcpServerIp)
                        {
                            foreach (ushort tcpServerPort in TCPServerPortList)
                            {
                                if (Self.SourcePort == tcpServerPort || Self.DestinationPort == tcpServerPort)
                                {
                                    if ((Self.SourceIp == tcpServerIp && Self.SourcePort == tcpServerPort) || (Self.DestinationIp == tcpServerIp && Self.DestinationPort == tcpServerPort))
                                    {
                                        return true;
                                    }
                                }
                            }
                        }
                    }
                }
                return false;
            }
        }
        public static TinyPacket TryParse(RawCapture rawCapture)
        {
            TinyPacket tinyPacket = new TinyPacket();
            if (rawCapture != null)
            {
                tinyPacket.RawCapture = rawCapture;
                var packet = PacketDotNet.Packet.ParsePacket(rawCapture.LinkLayerType, rawCapture.Data);
                if (packet is PacketDotNet.EthernetPacket)
                {
                    var eth = ((PacketDotNet.EthernetPacket)packet);
                    var ip = PacketDotNet.IpPacket.GetEncapsulated(packet);
                    if (ip != null)
                    {
                        tinyPacket.SourceIp = ip.SourceAddress.ToString();
                        tinyPacket.DestinationIp = ip.DestinationAddress.ToString();
                        tinyPacket.ProtocolType = ip.Protocol;
                        var tcp = PacketDotNet.TcpPacket.GetEncapsulated(packet);
                        if (tcp != null)
                        {
                            tinyPacket.PayloadData = tcp.PayloadData;
                            tinyPacket.DestinationPort = tcp.DestinationPort;
                            tinyPacket.SourcePort = tcp.SourcePort;
                            TinyIMPacket.ProcessTcpPacket(tinyPacket);
                        }

                        var udp = PacketDotNet.UdpPacket.GetEncapsulated(packet);
                        if (udp != null)
                        {
                            tinyPacket.PayloadData = udp.PayloadData;
                            tinyPacket.DestinationPort = udp.DestinationPort;
                            tinyPacket.SourcePort = udp.SourcePort;
                            TinyIMPacket.ProcessUdpPacket(tinyPacket);
                        }
                    }
                }
            }
            tinyPacket.Self = tinyPacket;
            return tinyPacket;
        }
        public void UpdateUIWithCapture(ListView listView, TinyPacket packet)
        {
            if (!IsListViewInitiated)
            {
                InitListViewList(listView);
                IsListViewInitiated = true;
            }

            //所有连接的集合由ListView的Items来表示
            //ListView每一行显示一个连接（源IP-端口和目的IP端口对应的连接）
            //连接用ListView的Columns来表示，所有连接第一列为会话ID，用会话ID来标识连接
            //每个ID对应一个包的列表或者一个包数据的字节数组，这里将每一行（Item）的Tag用来存储其对应的数据包或字节数组
            //每个连接对应一个包的列表或者一个包数据的字节数组
            //如果有包进入本函数，首先遍历集合中的连接，判断其是否为已有连接，
            //如果是已有连接就将包加入该连接的队列里（也可以将新包数据附加到已有包数据的结尾——借鉴QQAnalyzer）
            //如果不是，就创建一个连接，将其加入ListView
            if (listView.Items.Count == 0)
            {
                AddItemToListView(listView, packet);
                return;
            }
            //遍历连接
            bool isConnectionExist = false;
            ListViewItem existItem = new ListViewItem();
            foreach (ListViewItem item in listView.Items)
            {
                if (item.Tag != null)
                {
                    TinyPacket tinyPacket = item.Tag as TinyPacket;
                    if (tinyPacket != null)
                    {
                        if ((tinyPacket.SourceIp == packet.DestinationIp && tinyPacket.DestinationIp == packet.SourceIp) || (tinyPacket.SourceIp == packet.SourceIp && tinyPacket.DestinationIp == packet.DestinationIp))
                        {
                            isConnectionExist = true;
                            existItem = item;
                            break;
                        }
                    }
                }
            }
            if (isConnectionExist)
            {
                int result;
                if (int.TryParse(listView.Items[existItem.Index].SubItems[7].Text, out result))
                {
                    result += 1;
                }
                listView.Items[existItem.Index].SubItems[7].Text = result.ToString();
                List<byte[]> dataCollection = listView.Items[existItem.Index].SubItems[7].Tag as List<byte[]>;
                if (dataCollection != null)
                {
                    dataCollection.Add(packet.RawCapture.Data);
                }
                listView.Items[existItem.Index].SubItems[7].Tag = dataCollection;
            }
            else
            {
                AddItemToListView(listView, packet);
            }
        }
        private void AddItemToListView(ListView listView, TinyPacket packet)
        {
            ListViewItem packetItem = new ListViewItem();
            packetItem.Tag = packet;
            packetItem.Text = listView.Items.Count.ToString();
            packetItem.SubItems.Add(packet.ProtocolType.ToString());
            packetItem.SubItems.Add(packet.SourceIp);
            packetItem.SubItems.Add(packet.DestinationIp);
            packetItem.SubItems.Add(packet.SourcePort.ToString());
            packetItem.SubItems.Add(packet.DestinationPort.ToString());
            packetItem.SubItems.Add(packet.Time.ToLocalTime().ToShortTimeString());
            var dataCollection = new List<byte[]>();
            dataCollection.Add(packet.RawCapture.Data);
            packetItem.SubItems.Add(new ListViewItem.ListViewSubItem() { Text = "1", Tag = dataCollection });
            listView.Items.Add(packetItem);
        }
        private void InitListViewList(ListView listView)
        {
            if (listView != null)
            {
                listView.GridLines = true;
                listView.FullRowSelect = true;
                listView.View = View.Details;
                listView.Scrollable = true;
                listView.MultiSelect = false;
                listView.HeaderStyle = ColumnHeaderStyle.Clickable;
                listView.Visible = true;
                listView.Columns.Clear();
                listView.Items.Clear();
                listView.Columns.Add("会话索引", 65, HorizontalAlignment.Center);
                listView.Columns.Add("协议类型", 65, HorizontalAlignment.Center);
                listView.Columns.Add("源IP地址", 100, HorizontalAlignment.Center);
                listView.Columns.Add("目标IP地址", 100, HorizontalAlignment.Center);
                listView.Columns.Add("源端口", 60, HorizontalAlignment.Center);
                listView.Columns.Add("目标端口", 60, HorizontalAlignment.Center);
                listView.Columns.Add("捕捉时间", 85, HorizontalAlignment.Center);
                listView.Columns.Add("收发统计", 65, HorizontalAlignment.Center);

            }
        }
        public byte[] GetPayloadData()
        {
            return this.PayloadData;
        }

        public static void AddServerToServerDomainList(string ip, IPProtocolType protocol)
        {
            
            if (protocol == IPProtocolType.UDP)
            {
                int count = UDPServerDomainList.Count();
                string[] result = new string[count + 1];
                result[count] = ip;
                for (int i = 0; i < count; i++)
                {
                    result[i] = UDPServerDomainList[i];
                }
                UDPServerDomainList = result;
                //Force Update
                UDPServerIpList = GetIpByDomain(UDPServerDomainList);
            }
            else if (protocol == IPProtocolType.TCP)
            {
                int count = TCPServerDomainList.Count();
                string[] result = new string[count + 1];
                result[count] = ip;
                for (int i = 0; i < count; i++)
                {
                    result[i] = TCPServerDomainList[i];
                }
                TCPServerDomainList = result;
                //Force Update
                TCPServerIpList = GetIpByDomain(TCPServerDomainList);
            }
        }
    }
}
